package MTT::Libs::Alarm;

use Moose;

use Data::Dumper;
use utf8;

use POSIX qw(strftime);
use Encode qw(decode encode);
use Class::Date qw(now);
use HTML::Table;
use PDF::Reuse;
use MTT::Libs::Param::AlarmParam;
use MTT::Libs::Masks;

use MTT;

has alarmed => ( is => 'rw', isa => 'Bool' );
#has alarm_info => ( is => 'ro', isa => 'ArrayRef', default => sub { [] } );
has alarm_info => ( is => 'rw' );

has schema => ( is => 'rw', required => 1 );


has object => ( 
	is => 'ro', 
	isa => 'MTT::Schema::Result::Alarm',
	writer => 'set_object',
	handles => [ qw(id name cron_spec date_run state status mail_to mail_to_copy 
					mail_from subj message attachment) ]
);

has recipients => ( is => 'ro', isa => 'ArrayRef', writer => 'set_recipients', default => sub { [] } );
has switches => ( is => 'ro', isa => 'ArrayRef[Int]', writer => 'set_switches', default => sub { [] } );
has trunks => ( is => 'ro', isa => 'ArrayRef', writer => 'set_trunks', default => sub { [] } );
has params => ( is => 'ro', isa => 'ArrayRef[MTT::Libs::Param::AlarmParam]', writer => 'set_params', default => sub { [] } );


has start_time => ( is => 'rw' );
has end_time => ( is => 'rw' );

has props => ( is => 'rw', isa => 'HashRef' );


has source_table => ( is => 'rw' );
has source_table_prev => ( is => 'rw' );

my @props = qw(start_time end_time run_type type_alarm type_number interval interval_prev min hour day 
			   cron_spec mincall_asr mincall_acd amask bmask short subj 
			   trunks_do mail_from mail_to mail_to_copy message attachment);

sub BUILD {
	my ($self) = @_;

	unless ( $self->object ) {
		$self->set_object( $self->schema->resultset('Alarm')->new_result({}) );
	}
	return $self;
}

sub running {
	my ($self, $state) = @_;
	
	my $st = $self->state;
	return $st unless defined $state;

	$self->object->state( $state );
	$self->object->update();
	return $st;
}


sub ring_alarm {
	my ($self, $dbh) = @_;

	my %rings;
	my $start_time = now()->strftime('%Y-%m-%d %H:00:00');

	my $select = 'SELECT switch, trunk_in, trunk_out, protocol_type, c.a_number, a_mask, c.b_number, b_mask,'
				. ' time_start, time_end, duration_call, duration_answer, cause'
				. ' FROM calls_now c, (SELECT a_number, b_number FROM calls_now'
				. "	WHERE time_start >= date_sub('" . $start_time . "', INTERVAL " . $self->interval() . ' MINUTE )'
#				. '	WHERE DATE(time_start) = "2011-01-24" AND a_number != 0 AND trunk_in != "0.0.0.0"'
				. ' GROUP BY a_number, b_number HAVING COUNT(a_number) > 4 and COUNT(b_number) > 4) AS a'
				. ' WHERE c.a_number = a.a_number AND c.b_number = a.b_number AND c.a_number != 0 AND c.trunk_in != "0.0.0.0" AND c.trunk_out != ""'
				. " AND time_start >= date_sub('" . $start_time . "', INTERVAL " . $self->interval() . ' MINUTE )'
				. " AND time_start <= '" . $start_time . "'" 
#				. ' AND c.a_number = 74991813916 AND c.b_number = 78166621289'
				. ' ORDER BY a_number, b_number, switch, time_start';
#warn $select;
	my (@calls, $number, $time, $time_end);
	$number = '';
	my $count = 0;
	foreach my $row ( @{ $dbh->selectall_arrayref($select) } ) {
		next unless $row->[4];

		my $key = $row->[4] . '_' . $row->[6] . '_' . $row->[0];
#		unless ( $number eq "$row->[4]_$row->[6]" ) {
		unless ( $number eq $key ) {
			push @{ $rings{$number} }, @calls if $count > 4;

			$number = $key;
			$time = $row->[8];
			$time_end = $row->[9];
			$count = 0;
			@calls = ($row);
			$count++;
		}
		else {
			my ($d1, $d2, $d3) = ( Class::Date->new($time), Class::Date->new( $row->[8] ), Class::Date->new( $time_end ) );
			if ( $d2 - $d1 <= 1 && $d2 < $d3 ) {
				push @calls, $row;
				$count++;
			}
			else {
				if ($count > 4) {
					push @{ $rings{$number} }, @calls;
				}
				@calls = ();
				$count = 0;
			}
			$time = $row->[8];
			$time_end = $row->[9];
		}
	}


	push @{ $rings{$number} }, @calls if @calls && $count > 4;

	return \%rings;
}

sub run_check {
	my ($self, $dbh) = @_;

	my $is_alarmed = 0;
	my $alarm_type = $self->alarm_type();

	$dbh->{mysql_enable_utf8} = 1;

	if ( $alarm_type eq 'ring' ) {
		my $rings = $self->ring_alarm($dbh);

		my $header = [ (
			'N', 'Коммутатор', 'Вх.', 'Исх.', 'Протокол', 'А-номер', 'А-код', 'Б-номер', 'Б-код', 
			'Начало', 'Завершение', 'Длит. вызова', 'Длит. ответа', 'Причина'
		)];

		if ( $self->props->{attachment} eq 'html' || !$self->props->{attachment}) {
			my $result = '';
			foreach (sort keys %$rings ) {
				$result .= '<p>' . join(' - ', split(/_/, $_)) . '</p>';

				my $data = $rings->{$_};
				my $i = 0;
				foreach my $r (@$data) {
					unshift @$r, ++$i;
				}
				unshift @$data, $header;

				my $table = HTML::Table->new(
					-data => $data,
					-oddrowclass => 't0',
					-border => 1
				);
				$result .= $table->getTable();
			}

			if ( $result ) {
				$self->alarmed(1);
				$self->alarm_info($result);
			}
		}
		elsif ( $self->props->{attachment} eq 'pdf' ) {
			my $file = 'alarm_' . $self->id . '.pdf'; #( int rand(time) ) . '.pdf';
			prFile('/tmp/' . $file);
			prMbox(0, 0, 842, 595); 
#			prFont('C');
#			prFontSize(13);
			
			my ($x, $y) = (23, 575); 
			my $res = 0;
			foreach (sort keys %$rings ) {
				my $data = $rings->{$_};
				next unless @$data;

				$res++;

				prText($x, $y, encode('utf8', join("\t", @$header)));
				
				$y -= 20;
				if ( $y <= 13 ) {
					$y = 575;
					prPage();
				}

				foreach ( @$data ) {
					prText($x, $y, encode('utf8', join("\t", @$_)));

					$y -= 20;
					if ( $y <= 13 ) {
						$y = 575;
						prPage();
					}
				}
			}
			
			prEnd();

			if ( $res ) {
				open(PDF, '<', "/tmp/$file");
				binmode(PDF);
				undef $/;
				my $data = <PDF>;
				close PDF;
				
				$self->alarmed(1);
				$self->alarm_info( $data );
			}

		}
		elsif ( $self->props->{attachment} eq 'csv' || $self->props->{attachment} eq 'excel' ) {
			my $result = '';
			foreach (sort keys %$rings ) {
				my $data = $rings->{$_};

				if ( $self->props->{attachment} eq 'excel' ) {
					$result .= join(",", @$header) . "\r\n" if @$data;
				}
				else {
					$result .= join("\t", @$header) . "\r\n" if @$data;
				}

				my $i = 0;
				foreach my $r (@$data) {
					$result .= join("\t", (++$i, @$r)) . "\r\n";
				}
			}

			if ( $result ) {
				$self->alarmed(1);

				if ( $self->props->{attachment} eq 'excel' ) {
					my $file = '/tmp/alarm_' . $self->id;
					open(CSV, '>', $file);
					binmode(CSV, ':utf8');
					print CSV $result;
					close CSV;

					system('perl ' . MTT->path_to('my_script', 'csv2xls.pl') . " $file $file.xls"); 
					open(XLS, "$file.xls");
					binmode(XLS);
					undef $/;
					my $data = <XLS>;
					close XLS;

					$self->alarm_info( $data );
				}
				else {
					$self->alarm_info($result);
				}
			}
		}

		return;
	}
	
	my $res;
	eval { 
		if ( $alarm_type eq 'trunk' ) {
			my $select = 'SELECT DISTINCT t.company_name, t.city, r.switch, r.trunk, r.direction, r.type,'
						. ' r.NDEV, r.NBLOCKDEV, r.date_start, r.date_end, r.MATTEMPTBHCA_first, r.NANSW_first,';
			if ( $self->props->{type_alarm} eq 'ASR' ) {
				$select .= ' r.ASR_first, r.NER_first,'
						. ' r.date_start2, r.date_end2, r.MATTEMPTBHCA_second, r.NANSW_second,'
						. ' r.ASR_second, r.NER_second';
				
			}
			elsif ( $self->props->{type_alarm} eq 'ACD' ) {
				$select .= ' r.NSEIZREQ_first, r.NCONVSEC_first, r.AVGNCONVSEC_first,'
						. ' r.date_start2, r.date_end2, r.MATTEMPTBHCA_second, r.NANSW_second, r.NSEIZREQ_second,'
						. ' r.NCONVSEC_second, r.AVGNCONVSEC_second';
			}
			
			$select .= ' FROM ' . $self->source_table . ' r LEFT JOIN trunks t'
					. ' ON (t.trunk = r.trunk AND t.switch = r.switch AND DATE(r.date_start) = DATE(t.created))'
					. ' ORDER BY (r.ASR_second - r.ASR_first) DESC';
#print "$select\n";					

			$res = $dbh->selectall_arrayref($select); 
		}
		else {
#			my $select = 'SELECT DISTINCT t.company_name, t.city, r.switch, r.trunk, r.direction, r.type, r.b_mask,'
			my $select = 'SELECT DISTINCT m.operator, m.city, r.b_mask, r.trunk, t.company_name, t.city, r.direction, r.type,'
					. ' r.NDEV, r.NBLOCKDEV, r.date_start, r.date_end, r.MATTEMPTBHCA_first, r.NANSW_first,';
			
			if ( $self->props->{type_alarm} eq 'ASR' ) {
				$select .= ' r.ASR_first, r.NER_first,'
						. ' r.date_start2, r.date_end2, r.MATTEMPTBHCA_second, r.NANSW_second,'
						. ' r.ASR_second, r.NER_second';
			}
			elsif ( $self->props->{type_alarm} eq 'ACD' ) {
				$select .= ' r.NSEIZREQ_first, r.NCONVSEC_first, r.AVGNCONVSEC_first,'
						. ' r.date_start2, r.date_end2, r.MATTEMPTBHCA_second, r.NANSW_second, r.NSEIZREQ_second,'
						. ' r.NCONVSEC_second, r.AVGNCONVSEC_second';
			}
#			$select .= ' FROM ' . $self->source_table . ' r LEFT JOIN trunks t'
#					. ' ON (t.trunk = r.trunk AND t.switch = r.switch AND DATE(r.date_start) = DATE(t.created))'
#					. ' ORDER BY (r.ASR_second - r.ASR_first) DESC';

			$select .= ' FROM ' . $self->source_table . ' r' 
					. ' LEFT JOIN trunks t on (r.switch = t.switch AND r.trunk = t.trunk AND DATE(r.date_start) = DATE(t.created))'
					. ' LEFT JOIN masks m ON ' . ( $alarm_type eq 'mask' ? '(m.mask = r.b_mask)' : '(m.crc = r.crc)' );
#			$select .= ' LEFT JOIN trunks t on (r.switch = t.switch AND r.trunk = t.trunk AND DATE(r.date_start) = DATE(t.created))';		
			$select .= ' ORDER BY (r.ASR_second - r.ASR_first) DESC';
#print "$select\n";					

			$res = $dbh->selectall_arrayref($select); 
		}
	};

	
	if ( $res && @$res) {
		$self->alarmed(1);

		my $header;
		if ( $alarm_type eq 'trunk' ) {
			if ( $self->props->{type_alarm} eq 'ASR' ) {
				$header = [ (
					'N',
					'Оператор', 'Город', 'Коммутатор', 'Транк', 'Направление', 
					'Тип', 'Кол-во каналов', 'Кол-во блок. каналов', 'Дата начала', 'Дата окончания', 
					'Кол-во попыток вызовов', 'Кол-во удачных вызовов', 'ASR', 'NER',
					'Дата начала', 'Дата окончания', 
					'Кол-во попыток вызовов', 'Кол-во удачных вызовов', 'ASR', 'NER'
				) ];
			}
			elsif ( $self->props->{type_alarm} eq 'ACD' ) {
				$header = [ (
						'N',
						'Оператор', 'Город', 'Коммутатор', 'Транк', 'Направление', 
						'Тип', 
						'Кол-во каналов', 'Кол-во блок. каналов', 'Дата начала', 'Дата окончания', 
						'Кол-во попыток вызовов', 'Кол-во удачных вызовов', 'Длит. попыток вызовов', 
						'Длит. разговоров', 'Средняя прод. разговора', 
						'Дата начала', 'Дата окончания', 
						'Кол-во попыток вызовов', 'Кол-во удачных вызовов', 'Длит. попыток вызовов',
						'Длит. разговоров', 'Средняя прод. разговора'
					) ];
			}
		}
		elsif ( $alarm_type eq 'mask' || $alarm_type eq 'short' ) {
			if ( $self->props->{type_alarm} eq 'ASR' ) {
				$header = [ (
					'N',
					'Компания', 'Город', 'Б-код', 'Транк', 'Оператор', 'Город', 'Направление', 'Тип', 
					'Кол-во каналов', 'Кол-во блок. каналов', 'Дата начала', 'Дата окончания', 
					'Кол-во попыток вызовов', 'Кол-во удачных вызовов', 
					'ASR', 'NER',
					'Дата начала', 'Дата окончания', 
					'Кол-во попыток вызовов', 'Кол-во удачных вызовов', 
					'ASR', 'NER') 
				];
			}
			elsif ( $self->props->{type_alarm} eq 'ACD' ) {
				$header = [ (
					'N',
					'Компания', 'Город', 'Б-код', 'Транк', 'Оператор', 'Город', 'Направление', 'Тип', 
					'Кол-во каналов', 'Кол-во блок. каналов', 'Дата начала', 'Дата окончания', 
					'Кол-во попыток вызовов', 'Кол-во удачных вызовов', 'Длит. попыток вызовов', 
					'Длит. разговоров', 'Средняя прод. разговора',
					'Дата начала', 'Дата окончания', 
					'Кол-во попыток вызовов', 'Кол-во удачных вызовов', 'Длит. попыток вызовов',
						 'Длит. разговоров', 'Средняя прод. разговора') 
				];

			}

		}

		my $i = 0;
		foreach (@$res) {
			unshift @$_, ++$i;
		}

		unshift @$res, $header;

		if ( $self->props->{attachment} eq 'html' || !$self->props->{attachment} ) {
			my $table = HTML::Table->new(
				-data => $res,
				-oddrowclass => 't0',
				-border => 1
			);

			$self->alarm_info( $table->getTable() );
		}
		elsif ( $self->props->{attachment} eq 'csv' ) {
			my $csv = '';
			foreach (@$res) {
				$csv .= join("\t", @$_);
			}
			$self->alarm_info( $csv );
		}
	}

	return;
}

sub set_nexttime {
	my ($self) = @_;

	my $interval = $self->interval();

	my $dt = $self->schema->storage->dbh->selectrow_arrayref(
		'SELECT DATE_ADD( CAST( DATE_FORMAT(NOW(), "%Y-%m-%d %H:00:00") as DATETIME ), INTERVAL ' . $interval . ' + MINUTE(date_run) MINUTE ) FROM alarms WHERE id = ' . $self->id
	);

	$self->date_run( $dt->[0] );
	$self->object->update();
}

sub get_recipients {
	my ($self) = @_;

	my @recipients;
	foreach my $rec ( $self->schema->resultset('Recipient')->search({id_alarm => $self->id}) ) {
		push @recipients, $rec->id_user->email;
	}

	push @recipients, $self->mail_to;

	return @recipients;
}

sub interval {
	my ($self) = @_;

	my $interval = $self->props->{interval};

	if ( $interval =~ /^\d+$/ ) {
		return $interval;
	}

	if ( $interval eq 'quarter' ) {
		$interval = 15;
	}
	elsif ( $interval eq 'hour' ) {
		$interval = 60;
	}
	else {
		$interval = 60;
	}
	return $interval;
}

sub date_prev {
	my ($self) = @_;

	if ( $self->date_run ) {
		return $self->date_run->subtract(minutes => $self->interval());
	}
}

sub alarm_type {
	my ($self) = @_;
	
	if ( $self->props->{run_type} ) {
		return $self->props->{run_type};
	}
	else {
		if ( ( $self->props->{amask} || $self->props->{bmask} ) && !$self->props->{short} ) {
			return 'mask';
		}
		elsif ( $self->props->{short} ) {
			return 'short';
		}
		else {
			return 'trunk';
		}
	}
}

sub get_switch_names {
	my ($self) = @_;

	my @names;

	foreach ( @{ $self->switches } ) {
		my $switch = $self->schema->resultset('Switch')->find($_);
		push @names, $switch->host_code if $switch;
	}

	return @names;
}

sub second_interval {
	my ($self) = @_;
	return $self->props->{interval_prev} || 7;
}

sub process {
	my ($self, $p) = @_;

	$self->props( {} );
	foreach my $prop (@props) { 
		next if $prop eq 'trunks';
		$self->props->{$prop} = $p->{$prop} if $p->{$prop};
	}


	my $trunks = $p->{trunks};
	$trunks = [ $trunks ] unless ref $trunks;
	$self->props->{trunks} = join(',', @$trunks);

	$self->name( $p->{name} );

	my $status = $p->{status} ? 0 : 1;
	$self->status($status);

	$p->{start_time} = strftime('%Y-%m-%d %H:%M:%S', localtime)
		unless $p->{start_time};

	my $date_run = $p->{start_time};
	$date_run =~ s/^\s+//;
	$date_run =~ s/\s+$//;
	$date_run .= ':00' if $date_run =~ /\d{4}-\d\d-\d\d \d\d:\d\d$/;
	$self->date_run( $date_run );

	$self->cron_spec( $p->{cron_spec} );
#	$self->run_type( $p->{run_type} );
	$self->mail_to( $p->{mail_to} );
	$self->mail_to_copy( $p->{mail_to_copy} );
	$self->mail_from( $p->{mail_from} );
	$self->subj( $p->{subj} );
	$self->message( $p->{message} );
	$self->attachment( $p->{attachment} );
#	$self->object->update();


	my $params = [];
	foreach ( $self->schema->resultset('Param')->all() ) {
		if ( $p->{ $_->name } ) {
			my $param = MTT::Libs::Param::AlarmParam->new(
				schema => $self->schema,
				item   => $_,
				condition => $p->{ $_->name }
			); 
			push @$params, $param;
		}
	}

	$self->set_params( $params );

	my $recipients = $p->{recipient};
	if ($recipients) {
		$recipients = [ $recipients ] unless ref $recipients;
		$self->set_recipients( $recipients ) if @$recipients;
	}

	my $switches = $p->{switches_do};
	if ($switches) { 
		$switches = [ $switches ] unless ref $switches;
		$self->set_switches( $switches );
	}

	return $self;
}

sub save {
	my ($self) = @_;

#	eval {
#		$self->schema->txn_do( sub {
				if ( $self->object->id ) {
					$self->object->recipients->delete();
					$self->object->alarm_switches->delete();
					$self->object->alarm_params->delete();
					$self->object->alarm_props->delete();
				}

				$self->cron_spec('0');

				# Получатели
				my @recipients;
				foreach ( @{ $self->recipients } ) {
					push @recipients, { id_user => $_ };
				}

				# Параметры
				my @params;
				foreach ( @{ $self->params } ) {
					push @params, {id_param => $_->id, cond => $_->condition};
					#$self->object->set_alarm_params(\@params);
#					$self->object->new_related('alarm_params', {id_param => $_->id, cond => $_->condition});
				}

				# Свичи
				my @switches;
				foreach ( @{ $self->switches } ) {
					push @switches, {id_switch => $_};
				}

				unless ( $self->object->in_storage ) {
					$self->object->insert();
					$self->object->add_to_alarm_params($_) 	 foreach @params;
					$self->object->add_to_alarm_switches($_) foreach @switches;
					$self->object->add_to_recipients($_) 	 foreach @recipients;
				}
				else {
					$self->schema->resultset('Alarm')->find( $self->id )->update();
					$self->object->delete_related('recipients');
					$self->object->delete_related('alarm_switches');
					$self->object->delete_related('alarm_params');
					$self->object->add_to_alarm_params($_) 	 foreach @params;
					$self->object->add_to_alarm_switches($_) foreach @switches;
					$self->object->add_to_recipients($_) 	 foreach @recipients;
					$self->object->update();
				}

				$self->schema->resultset('AlarmProp')->search({id_alarm => $self->id})->delete();
				my $store = $self->schema->resultset('AlarmProp');
				foreach my $prop ( @props ) {
					$store->create({
							id_alarm => $self->object->id,
							prop => $prop,
							value => $self->props->{$prop}
						}) if $self->props->{$prop};
				}

#			}
#		);
#	};
#	if ($@) {
#		if ( $@ =~ /Rollback failed/ ) {
#			die "Somthing terrible has happined: $@";
#		}
#		die $@;
#	}
}

sub load {
	my ($self, $id) = @_;

	die "id need" unless $id;
	die "Object already exist [ ${\( $self->id )} ]" if $self->object->id;

	if ( my $alarm = $self->schema->resultset('Alarm')->find($id) ) {
		$self->set_object( $alarm );

		my $store = $self->schema->resultset('AlarmProp')->search({id_alarm => $alarm->id});

		$self->props( {} );
		foreach my $prop ( @props ) {
			if ( my $p = $store->find({id_alarm => $alarm->id, prop => $prop}) ) {
				$self->props->{ $p->prop } = $p->value;
			}
		}

#		if ( my $t = $self->date_from ) {
#			$t =~ s/^\s*(\d{4}.\d\d.\d\d).*(\d\d:\d\d).*$/$1 $2/;
#			$self->start_time($t);
#		}

#		if ( my $t = $self->date_to ) {
#			$t =~ s/^\s*(\d{4}.\d\d.\d\d).*(\d\d:\d\d).*$/$1 $2/;
#			$self->end_time($t);
#		}

		my $recipients = [];
		foreach ( $self->object->recipients->all() ) {
			push @$recipients, $_->id;
		}
		$self->set_recipients( $recipients );

		my $switches = [];
		foreach ( $self->object->alarm_switches->all() ) {
			push @$switches, $_->id_switch->id;
		}
		$self->set_switches( $switches );

		my $params = [];
		foreach ( $self->object->alarm_params->all() ) {
			my $param = new MTT::Libs::Param::AlarmParam schema => $self->schema, item => $_;
			push @$params, $param;
		}
		$self->set_params( $params );
		
	}
	else {
		die "Alarm with id $id not found";
	}

}

sub remove {
	my ($self) = @_;

	if ( $self->id ) {
		$self->schema->txn_do( sub {
				$self->object->alarm_props->delete();
				$self->object->alarm_params->delete();
				$self->object->alarm_switches->delete();
				$self->object->recipients->delete();
				$self->object->delete();
			}
		);
	}
	if ($@) {
		die $@;
	}
	
}

__PACKAGE__->meta->make_immutable;
1;
